﻿using System.IdentityModel.Tokens.Jwt;
using System.Security.Authentication;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;
using Jst.Application.Contracts.IApplication.Auth;
using Jst.Application.Contracts.IApplication.Auth.Dtos;
using Jst.Application.Contracts.IApplication.Auth.Vos;
using Jst.Application.Contracts.Services;
using Jst.Core.Utility;
using Jst.Domain.Entities;
using Jst.Domain.Shared.Enums;
using Jst.Domain.Shared.Models;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using SqlSugar;
using static Jst.Application.Contracts.IApplication.Auth.Dtos.AuthToken;

namespace Jst.Application.Auth
{
    /// <summary>
    /// 鉴权
    /// </summary>
    public class AuthServices : IAuthService
    {
        private readonly ISqlSugarClient _db;

        private readonly ICachingProvider _cachingProvider;

        public AuthServices(ISqlSugarClient db, ICachingProvider cachingProvider)
        {
            _db = db;
            _cachingProvider = cachingProvider;
        }




        /// <summary>
        /// 访问换取令牌
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        public async Task<ApiResult> UserloginAuthAsync(InputDto dto)
        {

            if (string.IsNullOrEmpty(dto.account.Trim()))
            {
                return ApiResult.Error(ResultCode.PARAM_ERROR, "账号不能为空");
            }
            if (string.IsNullOrEmpty(dto.password.Trim()))
            {
                return ApiResult.Error(ResultCode.PARAM_ERROR, "密码不能为空");
            }

            try
            {
                var result = await AuthenticateAsync(dto);
                return ApiResult.Success("认证成功", result);
            }
            catch (AuthenticationException ex)
            {
                return ApiResult.Error(ResultCode.LOGIN_ERROR, ex.Message);
            }
            catch (Exception ex)
            {
                return ApiResult.Error(ResultCode.SERVER_ERROR, "系统异常，请稍后重试");
            }
        }

        /// <summary>
        /// 刷新令牌
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        /// <exception cref="AuthenticationException"></exception>
        public async Task<OutputVo> RefreshTokenAsync(RefreshTokenDto dto)
        {
            // 1. 一次性解析
            var principal = ReadTokenPrincipal(dto.AccessToken, validateLifetime: false);
            if (principal is null) throw new AuthenticationException("无效令牌");

            // 2. 验证 RefreshToken
            var cacheKey = $"REFRESH_TOKEN:{principal.UserId}";
            var cached = await _cachingProvider.GetAsync(cacheKey);
            if (cached != dto.RefreshToken) throw new AuthenticationException("会话超时，请重新登录");

            // 3. 生成新令牌
            var newAccessToken = GenerateAccessToken(principal);
            var newRefreshToken = GenerateRefreshToken();
            await _cachingProvider.SetAsync(cacheKey, newRefreshToken,
                TimeSpan.FromMinutes(AppSettingsConstVars.RefreshExpires));

            return new OutputVo
            {
                AccessToken = newAccessToken,
                RefreshToken = newRefreshToken,
                UserId = long.Parse(principal.UserId),
                UserName = principal.UserName,
                ExpiresIn = AppSettingsConstVars.Expires
            };
        }

        /// <summary>
        /// 注销令牌
        /// </summary>
        /// <param name="accessToken"></param>
        /// <returns></returns>
        /// <exception cref="AuthenticationException"></exception>
        public async Task<bool> CancelTokenAsync(string accessToken)
        {
            var principal = ReadTokenPrincipal(accessToken, validateLifetime: false);
            if (principal is null) throw new AuthenticationException("无效令牌");

            try
            {
                // 1. 加入黑名单
                var jwt = new JwtSecurityTokenHandler().ReadJwtToken(accessToken);

                var expires = new DateTimeOffset(jwt.ValidTo);
                var currentTime = DateTimeOffset.UtcNow;
                var blackListSeconds = Math.Max(0, (int)(expires - currentTime).TotalSeconds);

                await _cachingProvider.SetAsync($"TOKEN_BLACKLIST:{principal.Jti}", "1",
                    TimeSpan.FromSeconds(blackListSeconds));

                // 2. 删除刷新令牌
                await _cachingProvider.DelAsync($"REFRESH_TOKEN:{principal.UserId}");
            }
            catch (Exception)
            {

                return false;
            }


            return true;
        }


        /// <summary>
        /// 生成令牌
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        /// <exception cref="AuthenticationException"></exception>
        private async Task<OutputVo> AuthenticateAsync(InputDto dto)
        {
            dto.password = CommonHelper.Md5For32(dto.password);

            var userInfo = await _db.Queryable<users>().Where(x => x.username.Equals(dto.account) && x.password.Equals(dto.password)).FirstAsync();
            if (userInfo == null)
            {
                throw new AuthenticationException("用户名或密码错误");
            }

            // 创建用户的声明集合
            var claims = new List<Claim>
            {
                new Claim(ClaimTypes.Name, userInfo.username),
                new Claim(JwtRegisteredClaimNames.Jti, userInfo.id.ToString()),
                new Claim(ClaimTypes.Role, userInfo.role_id.ToString()),
                new Claim(JwtRegisteredClaimNames.Iat, DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString()),
                new Claim(ClaimTypes.Expiration, DateTime.UtcNow.AddMinutes(AppSettingsConstVars.Expires).ToString())
            };

            // 创建身份主体
            var identity = new ClaimsIdentity(JwtBearerDefaults.AuthenticationScheme);
            identity.AddClaims(claims);

            var jwtToken = new JwtSecurityToken(
                issuer: AppSettingsConstVars.JwtConfigIssuer,
                audience: AppSettingsConstVars.JwtConfigAudience,
                claims: claims,
                expires: DateTime.UtcNow.AddMinutes(AppSettingsConstVars.Expires),
                signingCredentials: new SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8.GetBytes(AppSettingsConstVars.JwtConfigSecretKey)), SecurityAlgorithms.HmacSha256)
            );
            var accessToken = new JwtSecurityTokenHandler().WriteToken(jwtToken);
            var refreshToken = GenerateRefreshToken();

            _cachingProvider.SetAsync($"REFRESH_TOKEN:{userInfo.id}", refreshToken, TimeSpan.FromMinutes(AppSettingsConstVars.RefreshExpires));

            return new OutputVo
            {
                AccessToken = accessToken,
                RefreshToken = refreshToken,
                UserId = userInfo.id,
                UserName = userInfo.username,
                ExpiresIn = AppSettingsConstVars.Expires,

            };
        }

        /// <summary>
        /// 一次性获取令牌
        /// </summary>
        /// <param name="token"></param>
        /// <param name="validateLifetime"></param>
        /// <returns></returns>
        private TokenPrincipal? ReadTokenPrincipal(string token, bool validateLifetime = true)
        {
            var handler = new JwtSecurityTokenHandler();
            var parameters = new TokenValidationParameters
            {
                ValidateIssuer = true,
                ValidIssuer = AppSettingsConstVars.JwtConfigIssuer,
                ValidateAudience = true,
                ValidAudience = AppSettingsConstVars.JwtConfigAudience,
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = new SymmetricSecurityKey(
                    Encoding.UTF8.GetBytes(AppSettingsConstVars.JwtConfigSecretKey)),
                ValidateLifetime = validateLifetime,
                ClockSkew = TimeSpan.Zero
            };

            try
            {
                var principal = handler.ValidateToken(token, parameters, out var validatedToken);
                if (validatedToken is not JwtSecurityToken jwt ||
                    !jwt.Header.Alg.Equals(SecurityAlgorithms.HmacSha256, StringComparison.OrdinalIgnoreCase))
                    return null;

                return new TokenPrincipal(
                    UserId: principal.FindFirst(JwtRegisteredClaimNames.Jti)?.Value!,
                    UserName: principal.FindFirst(ClaimTypes.Name)?.Value!,
                    SystemId: principal.FindFirst("SystemId")?.Value ?? string.Empty,
                    Jti: principal.FindFirst(JwtRegisteredClaimNames.Jti)?.Value!);
            }
            catch
            {
                return null;
            }
        }

        /// <summary>
        /// 生成访问令牌
        /// </summary>
        /// <param name="principal"></param>
        /// <returns></returns>
        private string GenerateAccessToken(TokenPrincipal principal)
        {
            var claims = new List<Claim>
            {
                new(JwtRegisteredClaimNames.Jti, principal.Jti),
                new(JwtRegisteredClaimNames.Sub, principal.UserId),
                new(ClaimTypes.Name, principal.UserName),
                new("SystemId", principal.SystemId)
            };

            var key = new SymmetricSecurityKey(
                Encoding.UTF8.GetBytes(AppSettingsConstVars.JwtConfigSecretKey));
            var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

            var token = new JwtSecurityToken(
                issuer: AppSettingsConstVars.JwtConfigIssuer,
                audience: AppSettingsConstVars.JwtConfigAudience,
                claims: claims,
                expires: DateTime.UtcNow.AddMinutes(AppSettingsConstVars.Expires),
                signingCredentials: creds);

            return new JwtSecurityTokenHandler().WriteToken(token);
        }

        /// <summary>
        /// 生成安全的刷新令牌（Base64编码的32字节随机数）
        /// </summary>
        /// <returns></returns>
        private static string GenerateRefreshToken()
            => Convert.ToBase64String(RandomNumberGenerator.GetBytes(32));


    }


}
